package com.bang.daybreak.security.config;

import com.bang.daybreak.common.config.DBProperties;
import com.bang.daybreak.security.config.filter.JwtAuthenticationTokenFilter;
import com.bang.daybreak.security.config.handler.*;
import com.bang.daybreak.security.serivce.impl.MyUserDetailsServiceImpl;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

/**
 * <p>
 * security 配置核心类
 * </p>
 *
 * @version 1.0
 * @author: wdj
 * @date: 2019/8/8 0008 下午 20:55
 */
@Configuration
@EnableWebSecurity
// 控制权限注解
@EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled = true, prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 匿名用户访问返回 JSON 格式的数据给前端（否则为 html）
     */
    @Autowired
    private GuestAuthenticationEntryPoint guestAuthenticationEntryPoint;

    /**
     * 登录成功返回的 JSON 格式数据给前端（否则为 html）
     */
    @Autowired
    private IdentityAuthenticationSuccessHandler identityAuthenticationSuccessHandler;

    /* *
      * 登录失败返回的 JSON 格式数据给前端（否则为 html）
      */
    @Autowired
    private IdentityAuthenticationFailureHandler identityAuthenticationFailureHandler;

    /**
     * 注销成功返回的 JSON 格式数据给前端（否则为 登录时的 html）
     */
    @Autowired
    private IndentityLogoutSuccessHandler indentityLogoutSuccessHandler;

    /* *
      * 无权访问返回的 JSON 格式数据给前端（否则为 403 html 页面）
      */
    @Autowired
    private IdentityAccessDeniedHandler identityAccessDeniedHandler;

    /**
     * JWT 拦截器
     */
    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    /**
     * 因为UserDetailsService的实现类实在很多，这里设置一下我们要注入的实现类
     */
    @Autowired
    private MyUserDetailsServiceImpl userDetailsService;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());

    }

    /**
     * 表单登录.
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                // y游客
                .exceptionHandling().authenticationEntryPoint(guestAuthenticationEntryPoint)
                .and()
                // 无权访问 JSON 格式的数据
                .exceptionHandling().accessDeniedHandler(identityAccessDeniedHandler)
                .and()
                // 表单登录
                .formLogin()
                .loginProcessingUrl("/login")
                // 登录成功
                .successHandler(identityAuthenticationSuccessHandler)
                // 登录失败
                .failureHandler(identityAuthenticationFailureHandler)
                .permitAll()
                .and()
                // 登出功能
                .logout().logoutUrl("/loginout")
                .logoutSuccessHandler(indentityLogoutSuccessHandler)
                .permitAll()
                .and()
                // 记住我
                .rememberMe()
                .userDetailsService(userDetailsService)
                .tokenRepository(persistentTokenRepository())
                .tokenValiditySeconds(3000)

                .and()
                // 所有请求必须经过认证
                .authorizeRequests()
                // 对于获取token的rest api要允许匿名访问
                .antMatchers("/login", "/loginout", "/error/**", "/api/**").permitAll()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest()
                .authenticated()
                .and()
                .csrf().disable();
    }


    @Autowired
    private DBProperties properties;

    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        /**
         * 多数据源配置会导致无法往persistent_logins中插入数据。所以在这里重新指定.
         *
         */
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setJdbcUrl(properties.getMaster().getJdbcUrl());
        dataSource.setUsername(properties.getMaster().getUsername());
        dataSource.setPassword(properties.getMaster().getPassword());
        dataSource.setDriverClassName(properties.getMaster().getDriverClassName());

        tokenRepository.setDataSource(dataSource);
        //第一次打开，生成表后注释掉。或者直接手动创建表.
//        tokenRepository.setCreateTableOnStartup(true);
        return tokenRepository;
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }


    /**
     * 过滤掉不需要权限的url.
     *
     * @param web web
     * @throws Exception e
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/v2/**",
                "/test/**",
                "/webjars/**",
                "/login.html/**",
                "/swagger-resources/**",
                "/swagger-resources/**",
                "/swagger-ui.html/**"
        );
    }


}
